﻿using System.Runtime.CompilerServices;
using System.Runtime.Serialization;

namespace HelenServer.Grpc.Contracts;

[DataContract]
public class GrpcResult
{
    protected GrpcResult() { }

    public GrpcResult(
        OperationStatus status,
        string? message,
        List<FailedHistory> failedHistory)
    {
        Status = status;
        Message = message ?? string.Empty;
        FailedHistory = failedHistory;
    }

    [DataMember(Order = 1)]
    public OperationStatus Status { get; protected set; }

    [DataMember(Order = 2)]
    public string Message { get; protected set; } = string.Empty;

    [DataMember(Order = 3)]
    public List<FailedHistory> FailedHistory { get; } = new();

    public bool Succeed => Status == OperationStatus.Success;

    public static GrpcResult OK()
    {
        return new();
    }

    public static GrpcResult Failed(
        string message,
        [CallerLineNumber] int sourceLineNumber = 0,
        [CallerFilePath] string sourceFilePath = "",
        [CallerMemberName] string memberName = "",
        params object[] args)
    {
        var result = new GrpcResult()
        {
            Status = OperationStatus.Failed,
            Message = message,
        };

        result.FailedHistory.Add(new()
        {
            Caller = new Caller(memberName, sourceFilePath, sourceLineNumber),
            Message = message
        });

        return result;
    }

    public static GrpcResult Error(Exception exception, string? message = null)
    {
        var result = new GrpcResult()
        {
            Status = OperationStatus.Error,
#if DEBUG
            Message = exception.ToString(),
#else
            Message = message,
#endif
        };

        result.FailedHistory.Add(new()
        {
            Message = message ?? exception.Message,
            Exception = new(exception)
        });

        return result;
    }
}

[DataContract]
public class GrpcResult<T>
{
    protected GrpcResult() { }

    public GrpcResult(
        OperationStatus status,
        string? message,
        List<FailedHistory> failedHistory,
        T data)
    {
        Status = status;
        Message = message ?? string.Empty;
        FailedHistory = failedHistory;
        Data = data;
    }

    [DataMember(Order = 1)]
    public string Message { get; protected set; } = string.Empty;

    [DataMember(Order = 2)]
    public OperationStatus Status { get; protected set; }

    public bool Succeed => Status == OperationStatus.Success;

    [DataMember(Order = 3)]
    public T Data { get; protected set; } = default!;

    [DataMember(Order = 4)]
    public List<FailedHistory> FailedHistory { get; } = new();

    public static GrpcResult<T> OK(T data)
    {
        return new()
        {
            Data = data
        };
    }

    public static GrpcResult<T> Failed(
        string message,
        [CallerMemberName] string memberName = "",
        [CallerFilePath] string sourceFilePath = "",
        [CallerLineNumber] int sourceLineNumber = 0,
        params object[] args)
    {
        var result = new GrpcResult<T>()
        {
            Status = OperationStatus.Failed,
            Message = message,
        };

        result.FailedHistory.Add(new()
        {
            Caller = new Caller(memberName, sourceFilePath, sourceLineNumber),
            Message = message
        });

        return result;
    }

    public static GrpcResult<T> Error(Exception exception, string? message = null)
    {
        var result = new GrpcResult<T>()
        {
            Status = OperationStatus.Error,
#if DEBUG
            Message = exception.ToString(),
#else
            Message = message,
#endif
        };

        result.FailedHistory.Add(new()
        {
            Message = message ?? exception.Message,
            Exception = new(exception)
        });

        return result;
    }
}